package cn.skyquant.quant4j.jforex.strategy.jingqi;

import cn.skyquant.quant4j.jforex.sdk.strategy.InstrumentEntity;
import com.dukascopy.api.*;
import com.dukascopy.api.feed.IBarFeedListener;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Library("quant4j-jforex-sdk.jar;")
public class JingQi02 implements IStrategy, IBarFeedListener {

    protected IEngine engine;
    protected IConsole console;
    protected IHistory history;
    protected IContext context;
    protected IIndicators indicators;
    protected IAccount account;
    protected IDataService dataService;

    @Configurable("交易品种")
    public Instrument instrument = Instrument.EURUSD;

    @Configurable("开单倍率(最小仓位0.01的几倍)")
    public int amount = 1;

    @Configurable("运行间隔时间")
    public Period period = Period.ONE_MIN;

    @Configurable("操作方法")
    public JingQiType type = JingQiType.stopOrder;

    @Configurable("多单:STOP价格↓下限(0表示无下限)")
    public double buy_stop_lower = 0;

    @Configurable("多单:STOP价格↑上限(0表示无上限)")
    public double buy_stop_upper = 0;

    @Configurable("多单:limit价格↓下限(0表示无下限)")
    public double buy_limit_lower = 0;

    @Configurable("多单:limit价格↑上限(0表示无上限)")
    public double buy_limit_upper = 0;

    @Configurable("空单:STOP价格↓下限(0表示无下限)")
    public double sell_stop_lower = 0;

    @Configurable("空单:STOP价格↑上限(0表示无上限)")
    public double sell_stop_upper = 0;

    @Configurable("空单:limit价格↓下限(0表示无下限)")
    public double sell_limit_lower = 0;

    @Configurable("空单:limit价格↑上限(0表示无上限)")
    public double sell_limit_upper = 0;


    @Configurable("止盈点数(为0代表不设止盈)")
    public int tp_pip = 10;

    @Configurable("止损点数(为0代表不设止损)")
    public int sl_pip = 500;

    @Configurable("STOP间隔点数")
    public double jiange_stop = 5;

    @Configurable("STOP挂单数量(0表示不挂stop)")
    public int stop_num = 5;

    @Configurable("limit间隔点数")
    public double jiange_limit = 5;

    @Configurable("limit挂单数量(0表示不挂limit)")
    public int limit_num = 5;

    @Configurable("挂单时间(小时)")
    public int order_time = 1;


    @Configurable("订单提交日志")
    public boolean log_submit = false;
    @Configurable("订单成交日志")
    public boolean log_fill = false;
    @Configurable("订单平仓日志")
    public boolean log_close = false;
    @Configurable("订单取消日志")
    public boolean log_cancel = false;

    final int max_pip = 2000;//默认2000个点范围内做单
    InstrumentEntity ie;

    private static final String LABEL_PRE = "J";

    double _buy_stop_lower;
    double _buy_stop_upper;
    double _buy_limit_lower;
    double _buy_limit_upper;
    double _sell_stop_lower;
    double _sell_stop_upper;
    double _sell_limit_lower;
    double _sell_limit_upper;


    double[] buy_stop_s;
    double[] buy_limit_s;
    double[] sell_stop_s;
    double[] sell_limit_s;

    @Override
    public void onStart(IContext context) throws JFException {
        this.engine = context.getEngine();
        this.console = context.getConsole();
        this.history = context.getHistory();
        this.context = context;
        this.indicators = context.getIndicators();
        this.account = context.getAccount();
        this.dataService = context.getDataService();
        ie = new InstrumentEntity(instrument);
        context.subscribeToBarsFeed(instrument, period, OfferSide.ASK, this);
        ITick tick = history.getLastTick(instrument);
        if (buy_limit_lower == 0) {
            _buy_limit_lower = tick.getBid() - ie.pipValue * max_pip;
        } else {
            _buy_limit_lower = buy_limit_lower;
        }
        if (buy_limit_upper == 0) {
            _buy_limit_upper = tick.getAsk() + ie.pipValue * max_pip;
        } else {
            _buy_limit_upper = buy_limit_upper;
        }
        if (_buy_limit_upper != 0 && _buy_limit_lower != 0 && _buy_limit_upper < _buy_limit_lower) {
            console.getOut().println("多单limit上下限设置错误，请检查");
            context.stop();
            return;
        }

        if (buy_stop_lower == 0) {
            _buy_stop_lower = tick.getBid() - ie.pipValue * max_pip;
        } else {
            _buy_stop_lower = buy_stop_lower;
        }
        if (buy_stop_upper == 0) {
            _buy_stop_upper = tick.getAsk() + ie.pipValue * max_pip;
        } else {
            _buy_stop_upper = buy_stop_upper;
        }

        if (_buy_stop_upper != 0 && _buy_stop_lower != 0 && _buy_stop_upper < _buy_stop_lower) {
            console.getOut().println("多单stop上下限设置错误，请检查");
            context.stop();
            return;
        }

        if (sell_limit_lower == 0) {
            _sell_limit_lower = tick.getBid() - ie.pipValue * max_pip;
        } else {
            _sell_limit_lower = sell_limit_lower;
        }
        if (sell_limit_upper == 0) {
            _sell_limit_upper = tick.getAsk() + ie.pipValue * max_pip;
        } else {
            _sell_limit_upper = sell_limit_upper;
        }

        if (_sell_limit_upper != 0 && _sell_limit_lower != 0 && _sell_limit_upper < _sell_limit_lower) {
            console.getOut().println("空单limit上下限设置错误，请检查");
            context.stop();
            return;
        }

        if (sell_stop_lower == 0) {
            _sell_stop_lower = tick.getBid() - ie.pipValue * max_pip;
        } else {
            _sell_stop_lower = sell_stop_lower;
        }
        if (sell_stop_upper == 0) {
            _sell_stop_upper = tick.getAsk() + ie.pipValue * max_pip;
        } else {
            _sell_stop_upper = sell_stop_upper;
        }


        if (_sell_stop_upper != 0 && _sell_stop_lower != 0 && _sell_stop_upper < _sell_stop_lower) {
            console.getOut().println("空单stop上下限设置错误，请检查");
            context.stop();
            return;
        }

        buy_limit_s = initPriceArray(_buy_limit_lower, _buy_limit_upper, ie.pipValue * jiange_limit);
        buy_stop_s = initPriceArray(_buy_stop_lower, _buy_stop_upper, ie.pipValue * jiange_stop);
        sell_limit_s = initPriceArray(_sell_limit_lower, _sell_limit_upper, ie.pipValue * jiange_limit);
        sell_stop_s = initPriceArray(_sell_stop_lower, _sell_stop_upper, ie.pipValue * jiange_stop);
        if (amount < 1 || type == JingQiType.stopOrder) {
            context.stop();
            return;
        }
        if (type == JingQiType.stopAndCloseOrder) {
            List<IOrder> orderList = engine.getOrders(instrument).stream().filter(x -> x.getLabel().startsWith(LABEL_PRE) && x.getOrderCommand().isConditional()).collect(Collectors.toList());
            orderList.forEach(x -> {
                try {
                    x.close();
                } catch (JFException e) {

                }
            });
            context.stop();
            return;
        }
        console.getOut().println("JingQi启动");

    }

    private double[] initPriceArray(double lower, double upper, double jiange_price) {
        double price = lower;
        List<Double> price_List = new ArrayList<>();
        do {
            int a = (int) ((price) / jiange_price);
            double p_f = new BigDecimal(jiange_price * a).setScale(ie.scale, RoundingMode.HALF_UP).doubleValue();
            price_List.add(p_f);
            price += jiange_price;
        } while (price < upper);
        double[] arr = new double[price_List.size()];
        for (int i = 0; i < price_List.size(); i++) {
            arr[i] = price_List.get(i);
        }
        return arr;
    }

    @Override
    public void onBar(Instrument instrument, Period period, OfferSide offerSide, IBar bar) {
        try {
            process();
        } catch (JFException e) {
            return;
        }
    }

    public void process() throws JFException {
        ITimeDomain timeDomain0 = dataService.getOfflineTimeDomain(0);
        ITimeDomain timeDomain1 = dataService.getOfflineTimeDomain(-1);

        if (timeDomain0 == null || timeDomain1 == null) {
            return;
        }
        long time = context.getTime();

        //停盘时间不干活
        if (timeDomain1.getEnd() + period.getInterval() > time || time > timeDomain0.getStart() - period.getInterval()) {
            return;
        }
        ITick tick = history.getLastTick(instrument);

        List<IOrder> orderList = engine.getOrders(instrument);

        List<IOrder> buyList = orderList.stream()
                .filter(x -> x.getLabel().startsWith(LABEL_PRE))
                .filter(x -> x.getOrderCommand().isLong()).collect(Collectors.toList());

        List<IOrder> sellList = orderList.stream()
                .filter(x -> x.getLabel().startsWith(LABEL_PRE))
                .filter(x -> x.getOrderCommand().isShort()).collect(Collectors.toList());

        if (type == JingQiType.both || type == JingQiType.onlyLong) {
            processLong(tick, buyList);
        }
        if (type == JingQiType.both || type == JingQiType.onlyShort) {

            processShort(tick, sellList);
        }
    }

    //做多单
    private void processLong(ITick tick, List<IOrder> orderList) {
        //先做buy stop逻辑
        for (int i = 1; i < buy_stop_s.length; i++) {
            double b1 = buy_stop_s[i - 1];
            double b0 = buy_stop_s[i];
            if (b1 < tick.getAsk() && tick.getAsk() < b0) {
                for (int j = 0; j < stop_num && i + j < buy_stop_s.length; j++) {
                    double price = buy_stop_s[i + j];
                    long count = orderList.stream().filter(x -> String.valueOf(price).equals(x.getComment())).count();

                    if (count == 0) {
                        submitOrder(price, IEngine.OrderCommand.BUYSTOP, i + j);
                    }
                }
                break;
            }
        }
        //再做buy limit逻辑
        for (int i = 1; i < buy_limit_s.length; i++) {
            double b1 = buy_limit_s[i - 1];
            double b0 = buy_limit_s[i];
            if (b1 < tick.getAsk() && tick.getAsk() < b0) {
                for (int j = 1; j <= limit_num && i - j >= 0; j++) {
                    double price = buy_limit_s[i - j];
                    long count = orderList.stream().filter(x -> String.valueOf(price).equals(x.getComment())).count();
                    if (count == 0) {
                        submitOrder(price, IEngine.OrderCommand.BUYLIMIT, i - j);
                    }
                }
                break;
            }
        }
    }

    private void processShort(ITick tick, List<IOrder> orderList) {
        //先做sell stop逻辑
        for (int i = 1; i < sell_stop_s.length; i++) {
            double b1 = sell_stop_s[i - 1];
            double b0 = sell_stop_s[i];
            if (b1 < tick.getBid() && tick.getBid() < b0) {
                for (int j = 1; j <= stop_num && i - j >= 0; j++) {
                    double price = sell_stop_s[i - j];
                    long count = orderList.stream().filter(x -> String.valueOf(price).equals(x.getComment())).count();
                    if (count == 0) {
                        submitOrder(price, IEngine.OrderCommand.SELLSTOP, i - j);
                    }
                }
                break;
            }
        }
        //再做sell limit逻辑
        for (int i = 1; i < sell_limit_s.length; i++) {
            double b1 = sell_limit_s[i - 1];
            double b0 = sell_limit_s[i];
            if (b1 < tick.getBid() && tick.getBid() < b0) {
                for (int j = 0; j < limit_num && i + j < sell_limit_s.length; j++) {
                    double price = sell_limit_s[i + j];
                    long count = orderList.stream().filter(x -> String.valueOf(price).equals(x.getComment())).count();
                    if (count == 0) {
                        submitOrder(price, IEngine.OrderCommand.SELLLIMIT, i + j);
                    }
                }
                break;
            }
        }
    }


    final SimpleDateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmss");
    final SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private void submitOrder(double price, IEngine.OrderCommand orderCommand, int i) {
        String comment = String.valueOf(price);
        String label = LABEL_PRE + "_" + instrument.name() + "_" + dateFormat.format(context.getTime()) + "_" + orderCommand.name() + "_" + i;
        double sl = 0;
        if (sl_pip > 0) {
            sl = new BigDecimal(orderCommand.isLong() ? price - ie.pipValue * sl_pip : price + ie.pipValue * sl_pip).setScale(ie.scale, RoundingMode.HALF_UP).doubleValue();
        }
        double tp = 0;
        if (tp_pip > 0) {
            tp = new BigDecimal(orderCommand.isLong() ? price + ie.pipValue * tp_pip : price - ie.pipValue * tp_pip).setScale(ie.scale, RoundingMode.HALF_UP).doubleValue();
        }
        double _amount = ie.minAmount * amount;
        long gtc = context.getTime() + order_time * Period.ONE_HOUR.getInterval();
        try {
            engine.submitOrder(label, instrument, orderCommand, _amount, price, 5, sl, tp, gtc, comment);
        } catch (JFException e) {
            console.getErr().println("submit error:" + e.getMessage());
        }
    }

    @Override
    public void onTick(Instrument instrument, ITick tick) throws JFException {

    }

    @Override
    public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {

    }

    @Override
    public void onMessage(IMessage message) throws JFException {
        if (message.getOrder() == null) {
            return;
        }
        IOrder order = message.getOrder();
        if (!order.getLabel().startsWith(LABEL_PRE)) {
            return;
        }
        String comment = order.getComment();
        String label = order.getLabel();
        double sl = order.getStopLossPrice();
        double tp = order.getTakeProfitPrice();
        double amount = order.getAmount();
        double openPrice = order.getOpenPrice();
        switch (message.getType()) {
            case ORDER_SUBMIT_OK:
                if (log_submit) {
                    ITick tick = history.getLastTick(instrument);
                    String str = String.format("提交 %s 订单[%s],当前价[%s],挂单价格[%s],止损[%s],止盈[%s],仓量[%s],comment[%s],gtc=%s",
                            order.getOrderCommand().name(), label, order.getOrderCommand().isLong() ? tick.getBid() : tick.getAsk(), openPrice, sl, tp, amount, comment, dateFormat1.format(order.getGoodTillTime()));
                    console.getOut().println(str);
                }
                break;
            case ORDER_FILL_OK:
                if (log_fill) {
                    String str = String.format("成交 订单[%s],open[%s],止损[%s],止盈[%s],仓量[%s],comment[%s]", label, openPrice, sl, tp, amount, comment);
                    console.getInfo().println(str);
                }
                break;
            case ORDER_CLOSE_OK:
                if (log_close) {
                    double profit = order.getProfitLossInUSD();
                    double closePrice = order.getClosePrice();
                    String str = String.format("平仓 订单[%s],成本[%s],平仓[%s],盈利[%s],止损[%s],止盈止盈[%s],仓量[%s],comment[%s]", label, openPrice, closePrice, profit, sl, tp, amount, comment);
                    if (profit > 0) {
                        console.getNotif().println(str);
                    } else if (profit < 0) {
                        console.getErr().println(str);
                    } else if (profit == 0 && closePrice == 0) {
                        if (log_cancel) {
                            String str1 = String.format("订单取消 order[%s],open[%s]", label, openPrice);
                            console.getOut().println(str1);
                        }
                    }
                }
                break;
        }
    }

    @Override
    public void onAccount(IAccount account) throws JFException {
    }

    @Override
    public void onStop() throws JFException {
        ie = null;
    }

    enum JingQiType {
        onlyLong("只挂多单"),
        onlyShort("只挂空单"),
        both("多空都挂"),
        stopOrder("停止挂单"),
        stopAndCloseOrder("停止挂单并删除订单");
        String desc;

        JingQiType(String desc) {
            this.desc = desc;
        }

        @Override
        public String toString() {
            return desc;
        }
    }
}
